
import * as THREE from 'three';
import React from 'react';
import { Element } from "components/Three";
import { ToolType } from '../constants/ToolType';
import Controller from "../Controller";
import IToolModel from '../interface/IToolModel';
import getGLTFLoader from 'tools/loaders/getGLTFLoader';
import { GLTF } from '../Controller/State/Element';

/** 导入模型工具 */
export default function useImportModel(controller: Controller) {

    const [loading, setLoading] = React.useState(false);

    /** 模型样本 */
    const [sample, setSample] = React.useState<GLTF>();

    /** 选中得工具 */
    const selectedTool = controller.useMemo(s => s.selectedTool, []);

    /** 相机 */
    const camera = controller.useMemo(s => s.camera, []);

    /** 工具数据 */
    const toolData = controller.useMemo(s => s.toolData, []);

    /** 场景 */
    const scene = controller.useMemo(s => s.scene, []);

    /** 地板 */
    const floor = controller.useMemo(s => s.floor, []);

    /** 工具 */
    const data = React.useMemo(() => toolData.get(ToolType.模型) as IToolModel || undefined, [toolData])

    /** 模型文件 */
    const file = React.useMemo(() => data?.file, [data]);

    /** canvas 容器 */
    const cont = controller.useMemo(s => s.cont, []);

    // 删除模型样本
    React.useEffect(() => () => { sample?.scene.scene.removeFromParent() }, [sample])

    // 载入样本模型
    React.useEffect(() => {
        (async () => {
            // 加载新样本
            if (selectedTool !== ToolType.模型 || !cont || !file) return;
            try {
                const gltf = new GLTF(await getGLTFLoader(file.path), file.path);

                controller.state.scene?.add(gltf.scene.scene);

                setSample(gltf);
            } catch (error) {
                console.error(error);
            }
        })()
        return () => {
            setSample(undefined);
        }
    }, [cont, controller, file, selectedTool])

    // 移动模型样本
    React.useEffect(() => {
        if (selectedTool !== ToolType.模型 || !cont || !floor) return;

        const onmouseover = (event) => {
            if (loading) return;

            const width = cont.offsetWidth;
            const height = cont.offsetHeight;

            if (!camera || !scene) return;

            /*射线投射器*/
            const mouse = new THREE.Vector2();

            mouse.x = (event.clientX / width) * 2 - 1;
            mouse.y = -(event.clientY / height) * 2 + 1;

            const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);

            //将平面坐标系转为世界坐标系
            const raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());

            //得到点击的几何体
            const raycasters = raycaster.intersectObjects(scene.children);

            const point = (() => {
                for (let i = 0; i < raycasters.length; i++) {
                    if (raycasters[i].object.uuid === floor.uuid) {
                        return raycasters[i].point;
                    }
                }
            })()
            if (!point) return;

            sample?.scene.scene.position.set(point.x, point.y, point.z);
        }

        cont.addEventListener('mousemove', onmouseover);
        return () => {
            cont.removeEventListener('mousemove', onmouseover);
        }
    }, [camera, cont, floor, loading, sample?.scene.scene.position, scene, selectedTool])

    // 导入模型
    React.useEffect(() => {
        if (selectedTool !== ToolType.模型 || !cont || !file) return;

        const onclick = async () => {
            setLoading(true);
            try {
                const gltf = new GLTF(await getGLTFLoader(file.path), file.path);
                const element = new Element('gltf', gltf.scene.scene.uuid, gltf);

                gltf.scene.scene.position.set(
                    sample?.scene?.scene.position.x || 0,
                    sample?.scene?.scene.position.y || 0,
                    sample?.scene?.scene.position.z || 0);
                gltf.parser.options.path = file.path;

                controller.sceneAdd(element);
            } catch (error) {
                console.error(error)
            }
            setLoading(false);
        }

        cont.addEventListener('click', onclick);
        return () => {
            cont.removeEventListener('click', onclick);
        }
    }, [cont, selectedTool, file, controller, sample])
}